<?php

namespace Shahu\Common\common;

use Exception;
use support\Redis;

/***
 * 默认Redis 实现
 * Class DefaultRedisCache
 * @package Modules\Core\Utils\Cache
 */
class RedisCache
{
    /**过期时间*/
    public static int $five_minute = 5;

    public static int $two_hour = 120;

    public static int $five_hour = 300;

    public static int $one_day = 1440;

    public static int $two_day = 2880;

    public static int $five_day = 7200;


    public static int $twenty_day = 28800;

    /**
     * 设置key
     * @param string $key
     * @param mixed $value
     * @param int $minute
     * @return bool
     */
    public static function set(string $key, mixed $value, int $minute = 0): bool
    {
        $rtn = false;
        if (!empty($key)) {
            $res = Redis::set($key, $value, NULL, self::getTime($minute));
            if (!empty($res)) {
                $rtn = $res;
            }
        }
        return $rtn;
    }

    /**
     * 过期时间
     * @param int $minute
     * @return int
     */
    private static function getTime(int $minute = 0): int
    {
        if ($minute > 0) {
            return intval(bcmul($minute, 60, 0));
        }
        return self::$two_day;
    }

    /**
     * 根据key获取相应的值
     * @param $key
     * @return mixed
     */
    public static function get($key): mixed
    {
        $rtn = '';

        if (!empty($key)) {
            $rtn = Redis::get($key);
        }

        return $rtn;
    }

    /**
     * 将一个或多个值 value 插入到列表 key 的表尾
     * @param $key
     * @param $value_arr
     * @param int $minute
     * @return mixed
     */
    public static function rPush($key, $value_arr, int $minute = 0): mixed
    {
        $rtn = false;

        if (!empty($key) && !empty($value_arr)) {
            $rtn = Redis::rPush($key, ...$value_arr);

            if (!empty($rtn)) {
                $rtn = true;
                if ($minute > 0) {
                    static::expire($key, $minute);
                }
            }
        }
        return $rtn;
    }

    /**
     * 设置超时时间
     * @param $key
     * @param int $minute 分钟
     * @return bool
     */
    public static function expire($key, int $minute = 0): bool
    {
        $res = false;
        //设置
        if (!empty($key)) {
            $res = Redis::expire($key, self::getTime($minute));
        }
        return $res;
    }

    public static function lPush($key, $value_arr, $minute = 0): mixed
    {
        $rtn = false;

        if (!empty($key) && !empty($value_arr)) {
            $rtn = Redis::lPush($key, ...$value_arr);
            if (!empty($rtn)) {
                $rtn = true;
                if ($minute > 0) {
                    static::expire($key, $minute);
                }
            }
        }

        return $rtn;
    }


    /**
     * *****************************
     * list操作
     * *****************************
     */

    /**
     * 弹出第一个元素并删除
     * @param $key
     * @return string
     */
    public static function lPop($key): string
    {
        $rtn = '';
        if (!empty($key)) {
            $rtn = Redis::lPop($key);
        }
        return $rtn;
    }

    /**
     * 通过索引获取列表中的元素
     * @param $key
     * @param int $index
     * @return string
     */
    public static function lIndex($key, int $index = 0)
    {
        $rtn = '';
        if (!empty($key)) {
            $rtn = Redis::lIndex($key, $index);
        }
        return $rtn;
    }

    /**
     * -- 将列表 source 中的最后一个元素(尾元素)弹出，并返回给客户端
     * -- 将 source 弹出的元素插入到列表 destination ，作为 destination 列表的的头元素
     * @param $source_key
     * @param $bak_key
     * @return mixed
     */
    public static function rPopLPush($source_key, $bak_key): mixed
    {
        $rtn = '';
        if (!empty($source_key) && !empty($bak_key)) {
            $rtn = Redis::rpoplpush($source_key, $bak_key);
        }

        return $rtn;
    }

    /**
     * 返回哈希表中给定域的值
     * @param $key
     * @param $field
     * @return mixed
     */
    public static function hGet($key, $field): mixed
    {
        $rtn = '';
        if (!empty($key) && !empty($field)) {
            $rtn = Redis::hGet($key, $field);
        }
        return $rtn;
    }

    public static function hMGet($key, array $field_arr = []) : array
    {
        //返回数组
        $return_arr = [];
        //key和字段不能为空
        if (!empty($key) && !empty($field_arr)) {
            //防止hMget性能问题，分批查询
            foreach (array_chunk($field_arr, 20) as $arr_chunk) {
                //查询
                $result_chunk = Redis::hMGet($key, $arr_chunk);
                //需要判断返回值是否是false，false去掉
                foreach ($result_chunk as $key => $value) {
                    if ($value !== false) {
                        $return_arr[$key] = $value;
                    }
                }
            }
        }
        return $return_arr;
    }


    /**
     * *****************************
     * hash操作
     * *****************************
     */

    /**
     * 同时将多个 field-value (域-值)对设置到哈希表 key 中
     * @param string $key
     * @param array $data
     * @param int $minute
     * @return mixed
     */
    public static function hMset(string $key = '', array $data = [], int $minute = 0): mixed
    {
        $rtn = false;

        if (!empty($key) && !empty($data)) {
            $rtn = Redis::hMSet($key, $data);

            if ($rtn && $minute > 0) {
                static::expire($key, $minute);
            }
        }

        return $rtn;
    }

    /**
     * 返回哈希表中，所有的字段和值
     * @param string $key
     * @return mixed
     */
    public static function hGetAll(string $key): mixed
    {
        $rtn = [];
        if (!empty($key)) {
            $rtn = Redis::hGetAll($key);
        }
        return $rtn;
    }

    /**
     * 删除哈希表 key 中的一个指定字段
     * @param $key
     * @param $field
     * @return bool
     */
    public static function hDel($key, $field): bool
    {
        $rtn = false;
        if (!empty($key) && !empty($field)) {
            if (is_array($field)) {
                $res = Redis::hDel($key, ...$field);
            } else {
                $res = Redis::hDel($key, $field);
            }
            $rtn = (bool)$res;
        }
        return $rtn;
    }

    /**
     * 返回集合 key 中的所有成员,不存在的 key 被视为空集合
     * @param $key
     * @return mixed
     */
    public static function sMembers($key): mixed
    {
        $rtn = [];
        if (!empty($key)) {
            $rtn = Redis::smembers($key);
        }
        return $rtn;
    }

    /**
     * 检查元素是否在set中
     * @param $key
     * @param $value
     * @return bool
     */
    public static function sIsMember($key, $value): bool
    {
        if (!empty($key) && !empty($value)) {
            return Redis::sIsMember($key, $value);
        }
        return false;
    }



    /************************************
     * **********************************
     * set操作
     * **********************************
     ***********************************/

    /**
     * 将一个元素加入到集合中，已经存在的member元素将被忽略
     * @param $key
     * @param $value
     * @param int $minute
     * @return mixed
     */
    public static function sAdd($key, $value, int $minute = 0): mixed
    {
        $rtn = false;

        if (!empty($key) && isset($value)) {
            if (is_array($value)) {
                $rtn = Redis::sAdd($key, ...$value);
            } else {
                $rtn = Redis::sAdd($key, $value);
            }
            if ($rtn) {
                $rtn = true;
                self::expire($key, $minute);
            }
        }

        return $rtn;
    }

    /**
     * 向有序集合添加一个或多个成员，或者更新已存在成员的分数
     * @param string $key
     * @param array $score_and_value
     * @param int $minute
     * @return bool
     */
    public static function zAdd(string $key, array $score_and_value = [], int $minute = 0): bool
    {
        $rtn = false;

        if (!empty($key) && !empty($score_and_value)) {
            $res = Redis::zAdd($key, ...$score_and_value);

            if ($res) {
                if ($minute > 0) {
                    static::expire($key, $minute);
                }
                $rtn = true;
            }
        }
        return $rtn;
    }

    /**
     * 按照正序返回有序集中指定区间内的成员
     * @param      $key
     * @param int $start
     * @param int $stop
     * @return array
     */
    public static function zRange($key, int $start = 0, int $stop = -1): array
    {
        $rtn = [];

        if (!empty($key)) {
            $rtn = Redis::zRange($key, $start, $stop);
        }

        return $rtn;
    }

    /************************************
     * **********************************
     * 有序集合
     * **********************************
     ***********************************/

    /**
     * 按照倒序返回有序集中指定区间内的成员
     * @param $key
     * @param int $start
     * @param int $stop
     * @return mixed
     */
    public static function zRevRange($key, int $start = 0, int $stop = -1): array
    {
        $rtn = [];
        if (!empty($key)) {
            $rtn = Redis::zRevRange($key, $start, $stop);
        }
        return $rtn;
    }

    /**
     * 移除一个有序集成员
     * @param $key
     * @param $member
     * @return bool
     */
    public static function zRem($key, $member): bool
    {
        $rtn = false;

        if (!empty($key) && !empty($member)) {
            if (is_array($member)) {
                $rtn = Redis::zrem($key, ...$member);
            } else {
                $rtn = Redis::zrem($key, $member);
            }
            if (!empty($rtn)) {
                $rtn = true;
            }
        }

        return $rtn;
    }

    /**
     * @param $key
     * @param $start
     * @param $end
     * @param array $options
     * @return array
     */
    public static function zRangeByScore($key, $start, $end, array $options = []): array
    {
        $rtn = [];
        if (!empty($key) && !empty($start)) {
            $rtn = Redis::zRangeByScore($key, $start, $end, $options);
        }
        return $rtn;
    }


    /**
     * @param $key
     * @param $start
     * @param $end
     * @param array $options
     * @return array
     */
    public static function zRevRangeByScore($key, $start, $end, array $options = []): array
    {
        $rtn = [];
        if (!empty($key) && !empty($start)) {
            $rtn = Redis::zRevRangeByScore($key, $start, $end, $options);
        }
        return $rtn;
    }


    /**
     * 设置唯一Key值并设定超时分钟
     * 当$value为空时，会默认将超时时间作为值
     * @param string $key
     * @param mixed $value
     * @param int $minute
     * @return bool
     */
    public static function setnx(string $key, mixed $value = 'lock', int $minute = 1): bool
    {
        $rtn = false;
        //操作
        if (!empty($key)) {
            try {
                $rtn = Redis::setnx($key, $value);
                if ($rtn) {
                    static::expire($key, $minute);
                }
            } catch (Exception $e) {
                static::del($key);
            }
        }

        return $rtn;
    }

    /**
     * 删除对应的值
     * @param $key
     * @return bool
     */
    public static function del($key): bool
    {
        $rtn = false;
        if (!empty($key)) {
            if (static::exists($key) === true) {
                $rtn = Redis::del($key) === 1;
            }
        }

        return $rtn;
    }


    /************************************
     * **********************************
     * 未整改
     * **********************************
     ***********************************/

    /**
     * 判断Key是否存在
     * @param $key
     * @return bool
     */
    public static function exists($key): bool
    {
        $rtn = false;
        if (!empty($key)) {
            $rtn = Redis::exists($key) === 1;
        }

        return $rtn;
    }

    /**
     * 仅且当域field尚未存在于哈希表的情况下，将它的值设置为value
     * @param $key
     * @param $field
     * @param $value
     * @param int $minute
     * @return mixed
     */
    public static function hSetnx($key, $field, $value, int $minute = 0)
    {
        $rtn = false;
        $time = 0;

        if (!empty($minute) && is_numeric($minute) && intval($minute) >= 0) {
            $time = intval($minute) * 60;
        }

        if (empty($time)) {
            $time = static::$two_day;
        }

        if (!empty($key) && !empty($field) && !is_null($value)
            && !empty($time) && is_numeric($time) && intval($time) > 0) {

            if (is_array($value)) {
                $value = json_encode($value);
            }

            $rtn = Redis::hsetnx($key, $field, $value);
            static::expire($key, $minute);

            if (!empty($rtn)) {
                $rtn = true;
            }
        }

        return $rtn;
    }


    /**
     * 移除并返回列表 key 的尾元素
     * @param $key
     * @param bool $isCluster
     * @return mixed
     */
    public static function rPop($key, bool $isCluster = false)
    {
        $rtn = '';

        if (!empty($key)) {

            if ($isCluster === true) {
                $key = static::recombination($key);
            }

            $rtn = Redis::rpop($key);
        }

        return $rtn;
    }

    /**
     * 兼容集群重组redis key
     * @param string $key
     * @return string
     */
    private static function recombination(string $key)
    {
        $rtn = '';
        if (!empty($key)) {
            $keys = explode('_', $key);
            if (sizeof($keys) > 1) {
                foreach ($keys as $k => &$v) {
                    if ($k == sizeof($keys) - 1) {
                        $v = '{' . $v . '}';
                    }
                    $rtn = implode('_', $keys);
                }
            }
        }

        return $rtn;

    }

    public static function lRem($key, $count, $value, $isCluster = false)
    {
        $rtn = 0;

        if (!empty($key) && is_numeric($count) && !empty($value)) {

            if ($isCluster === true) {
                $key = static::recombination($key);
            }

            $rtn = Redis::lrem($key, $count, $value);
        }

        return $rtn;
    }

    /**
     * SPOP 命令从存储在 key 的集合中移除并返回一个或多个随机元素。该操作与 SRANDMEMBER 类似，它从一个集合中返回一个或多个随机元素，但 SRANDMEMBER 不删除元素
     * @param $key
     * @param $count
     * @return mixed
     */
    public static function sPopByCount($key, $count)
    {
        $rtn = '';

        if (!empty($key) && is_numeric($count) && intval($count) > 0) {
            $rtn = Redis::sPop($key, $count);
        }

        return $rtn;
    }

    /**
     * 移除并返回集合中的一个随机元素
     * @param $key
     * @return mixed
     */
    public static function sPop($key)
    {
        $rtn = '';

        if (!empty($key)) {

            $rtn = Redis::sPop($key);
        }

        return $rtn;
    }

    /**
     * 返回列表 key 的长度
     * @param $key
     * @return mixed
     */
    public static function lLen($key, $isCluster = false)
    {
        $rtn = '';

        if (!empty($key)) {
            if ($isCluster === true) {
                $key = static::recombination($key);
            }

            $rtn = Redis::llen($key);
        }

        return $rtn;
    }

    /**
     * 返回哈希表 key 中域的数量
     * @param $key
     * @return mixed
     */
    public static function hLen($key)
    {
        $rtn = '';

        if (!empty($key)) {
            $rtn = Redis::hlen($key);
        }

        return $rtn;
    }

    /**
     * 为哈希表 key 中的域 field 的值加上增量 increment
     * @param $key
     * @param $field
     * @param $number
     * @param int $minute
     * @return mixed
     */
    public static function hIncrBy($key, $field, $number, int $minute = 0)
    {
        $rtn = 0;

        if (!empty($key) && !empty($field) && !empty($number) && is_numeric($number)) {
            try {
                $rtn = Redis::hincrby($key, $field, $number);
                if (!empty($minute) && (int)$minute > 0) {
                    static::expire($key, $minute);
                }
            } catch (Exception $exception) {
                $rtn = false;
            }
        }

        return $rtn;
    }

    /**
     * Redis Srem 命令用于移除集合中的一个或多个成员元素，不存在的成员元素会被忽略
     * @param $key
     * @param $member
     * @return mixed
     */
    public static function sRem($key, $member)
    {
        $rtn = false;

        if (!empty($key) && !empty($member)) {

            $rtn = Redis::srem($key, $member);

            if (!empty($rtn)) {
                $rtn = true;
            }
        }

        return $rtn;
    }

    /**
     * 返回集合 key 的基数(集合中元素的数量)
     * @param $key
     * @return mixed
     */
    public static function sCard($key)
    {
        $rtn = 0;

        if (!empty($key)) {
            $rtn = Redis::scard($key);
        }

        return $rtn;
    }

    /**
     * 获取有序集合的成员数
     * @param $key
     * @return int
     */
    public static function zCard($key)
    {
        $rtn = 0;

        if (!empty($key)) {
            $rtn = Redis::zcard($key);
        }

        return $rtn;
    }

    /**
     *
     * @param $key
     * @return mixed
     */
    public static function keys($key)
    {
        $rtn = [];

        if (!empty($key)) {
            $rst = Redis::keys($key);

            if (!empty($rst) && is_array($rst)) {
                $rtn = $rst;
            }
        }

        return $rtn;
    }

    /**
     * Redis Keys 命令用于查找所有符合给定模式 pattern 的 key
     * @param $key
     * @return mixed
     */
    public static function hKeys($key)
    {
        $rtn = [];

        if (!empty($key)) {
            $rst = Redis::hkeys($key);

            if (!empty($rst) && is_array($rst)) {
                $rtn = $rst;
            }
        }

        return $rtn;
    }

    /**
     * key 中储存的数字加上指定的增量值
     * @param $key
     * @param $amount
     * @param int $minute
     * @return mixed|void
     */
    public static function incrBy($key, $amount, int $minute = 0)
    {
        $rtn = 0;

        if (!empty($key) && !empty($amount) && is_numeric($amount) && intval($amount) > 0) {
            $res = Redis::incrby($key, $amount);
            if (!empty($minute)) {
                static::expire($key, $minute);
            }
            if (!empty($res) && is_numeric($res)) {
                $rtn = intval($res);
            }
        }

        return $rtn;
    }

    /**
     * key 所储存的值减去给定的减量值
     * @param $key
     * @param $amount
     * @param $minute
     * @return mixed|void
     */
    public static function decrBy($key, $amount, $minute = 0)
    {
        $rtn = 0;

        if (!empty($key) && !empty($amount) && is_numeric($amount) && intval($amount) > 0) {
            $res = Redis::decrby($key, $amount);
            if (!empty($minute) && intval($minute) > 0) {
                static::expire($key, $minute);
            }
            if (!empty($res) && is_numeric($res)) {
                $rtn = intval($res);
            }
        }

        return $rtn;
    }

    /**
     * 判断字符串是否以strMark开始
     * @param $str
     * @param $strMark
     * @return bool
     */
    public static function startWith($str, $strMark)
    {
        $str = trim($str);

        if (!empty($str) && !empty($strMark)) {
            return strpos($str, $strMark) === 0;
        }

        return false;
    }

    /**
     * 判断字符串是否以strMark结束
     * @param $str
     * @param $strMark
     * @return bool
     */
    public static function endWith($str, $strMark)
    {
        $str = trim($str);

        if (!empty($str) && !empty($strMark)) {
            $pos = strripos($str, $strMark);
            return $pos !== false && $pos == strlen($str) - strlen($strMark);
        }

        return false;
    }

    /**
     * scan 扫码key
     * @param $cursor int 游标位置
     * @param $pattern string 正则表达式
     * @param $count   int 条数
     * @return mixed
     */
    public static function scan($cursor, $pattern = null, $count = 100)
    {
        $rtn = [];
        if (!empty($pattern)) {
            $rtn = Redis::scan($cursor, 'match', $pattern, 'count', $count);
        } else {
            $rtn = Redis::scan($cursor, 'count', $count);
        }
        return $rtn;

    }

    /**
     * 返回列表中指定区间内的元素
     * @param $key
     * @param $start
     * @param $end
     * @return array|mixed
     */
    public static function lRange($key, $start, $end): mixed
    {
        $rtn = [];

        if (!empty($key) && is_numeric($start) && is_numeric($end)) {
            $rtn = Redis::lRange($key, $start, $end);
        }

        return $rtn;
    }

}
